#############################################################
# 
# 
# 
# 
# make file based on bsp.
# 
# 
# 
# 
#############################################################
#final output program prefix.
PROGRAM       ?= artem_muse_firmware
# debug release
CONFIGURATION ?= debug
# scratchpad default
LINK_TARGET   ?= scratchpad

#############################################################
# Makefile Arguments
#############################################################

# BSP_DIR sets the path to the target-specific board support package.
# BSP_DIR ?= $(abspath bsp)
BSP_DIR = bsp
# SRC_DIR sets the path to the program source directory
SRC_DIR = src

SOURCE_DIRS = $(SRC_DIR) \
	$(SRC_DIR)/gloss \
	$(SRC_DIR)/include \
	$(SRC_DIR)/include/metal \
	$(SRC_DIR)/include/metal/drivers \
	$(SRC_DIR)/include/metal/machine \
	$(SRC_DIR)/metal \
	$(SRC_DIR)/metal/drivers \
	$(SRC_DIR)/src \
	$(SRC_DIR)/muse \
	$(SRC_DIR)/src/drivers

#############################################################
# BSP loading
#############################################################

# There must be a settings makefile fragment in the BSP's board directory.
ifeq ($(wildcard $(BSP_DIR)/settings.mk),)
$(error Unable to find $(BSP_DIR)/settings.mk)
endif

# Include the BSP settings
include $(BSP_DIR)/settings.mk

# Check that settings.mk sets RISCV_ARCH and RISCV_ABI
ifeq ($(RISCV_ARCH),)
$(error $(BSP_DIR)/board.mk must set RISCV_ARCH, the RISC-V ISA string to target)
endif

ifeq ($(RISCV_ABI),)
$(error $(BSP_DIR)/board.mk must set RISCV_ABI, the ABI to target)
endif

ifeq ($(RISCV_CMODEL),)
RISCV_CMODEL = medany
endif

ifeq ($(RISCV_LIBC),)
RISCV_LIBC=nano
SPEC=nano
endif

# Determines the XLEN from the toolchain tuple
ifeq ($(patsubst rv32%,rv32,$(RISCV_ARCH)),rv32)
RISCV_XLEN := 32
else ifeq ($(patsubst rv64%,rv64,$(RISCV_ARCH)),rv64)
RISCV_XLEN := 64
else
$(error Unable to determine XLEN from $(RISCV_ARCH))
endif

#############################################################
# Toolchain
#############################################################

# Allow users to select a different cross compiler.
CROSS_COMPILE ?= riscv64-unknown-elf

RISCV_GCC     := $(CROSS_COMPILE)-gcc
RISCV_GXX     := $(CROSS_COMPILE)-g++
RISCV_OBJDUMP := $(CROSS_COMPILE)-objdump
RISCV_OBJCOPY := $(CROSS_COMPILE)-objcopy
RISCV_GDB     := $(CROSS_COMPILE)-gdb
RISCV_AR      := $(CROSS_COMPILE)-ar
RISCV_SIZE    := $(CROSS_COMPILE)-size

#############################################################
# Software Flags
#############################################################

# Set the arch, ABI, and code model
RISCV_CFLAGS    += -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -mcmodel=$(RISCV_CMODEL)
RISCV_ASFLAGS   += -march=$(RISCV_ARCH) -mabi=$(RISCV_ABI) -mcmodel=$(RISCV_CMODEL)
# Prune unused functions and data
RISCV_CFLAGS    += -ffunction-sections -fdata-sections
# Include the Metal headers
RISCV_CFLAGS    += -I $(SRC_DIR) -I $(SRC_DIR)/include
# Reference selected library
RISCV_ASFLAGS   += --specs=$(SPEC).specs
RISCV_CFLAGS    += --specs=$(SPEC).specs

# gcc10 default use -fno-common, but libmetal has lots of multiple definitions
RISCV_CFLAGS    += -fcommon

# Turn on garbage collection for unused sections
RISCV_LDFLAGS   += -Wl,--gc-sections
# Turn on linker map file generation
RISCV_LDFLAGS   += -Wl,-Map,$(PROGRAM).map
# Turn off the C standard library
RISCV_LDFLAGS   += -nostartfiles -nostdlib
# Link to the relevant libraries
RISCV_LDLIBS    += -Wl,--start-group -lc -lgcc -lm -Wl,--end-group


ifeq ($(CONFIGURATION), debug)
RISCV_ASFLAGS   += -O0 -g
RISCV_CFLAGS    += -O0 -g
else ifeq ($(CONFIGURATION), release)
RISCV_ASFLAGS   += -Os
RISCV_CFLAGS    += -Os
else
$(error CONFIGURATION=$(CONFIGURATION) is invalid)
endif

#############################################################
# Software
#############################################################

PROGRAM_ELF ?= $(CONFIGURATION)/$(PROGRAM).elf
PROGRAM_HEX ?= $(CONFIGURATION)/$(PROGRAM).hex
PROGRAM_LST ?= $(CONFIGURATION)/$(PROGRAM).lst

PROGRAM_C_FILES = $(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.c))
PROGRAM_S_FILES = $(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.S))

PROGRAM_C_OBJS  = $(addprefix $(CONFIGURATION)/, ${patsubst %.c, %.o, $(PROGRAM_C_FILES)})
PROGRAM_S_OBJS  = $(addprefix $(CONFIGURATION)/, ${patsubst %.S, %.o, $(PROGRAM_S_FILES)})

.PHONY: all
all: $(PROGRAM_ELF) $(PROGRAM_HEX)

code/include/metal/machine/inline.h: bsp/metal-inline.h
	rm $@
	cp $< $@
code/include/metal/machine/platform.h: bsp/metal-platform.h
	rm $@
	cp $< $@
code/include/metal/machine.h: bsp/metal.h
	rm $@
	cp $< $@

$(CONFIGURATION)/%.d: %.c
	@mkdir -p $(dir $@)
	@echo -n $(dir $@) > $@
	$(RISCV_GCC) -MM $(RISCV_CFLAGS) $< >> $@
	@echo "	$(RISCV_GCC) $(RISCV_CFLAGS) -c $< -o $@" | sed 's/\.d/\.o/g'>> $@

#include $(PROGRAM_C_OBJS:.o=.d)

$(PROGRAM_C_OBJS): $(CONFIGURATION)/%.o: %.c
	@mkdir -p $(dir $@)
	$(RISCV_GCC) $(RISCV_CFLAGS) -S -fverbose-asm $^ -o $@.S
	$(RISCV_GCC) $(RISCV_CFLAGS) -c $^ -o $@


$(PROGRAM_S_OBJS): $(CONFIGURATION)/%.o: %.S
	@mkdir -p $(dir $@)
	$(RISCV_GCC) $(RISCV_CFLAGS) -c $^ -o $@

$(PROGRAM_ELF): $(PROGRAM_C_OBJS) $(PROGRAM_S_OBJS) $(BSP_DIR)/metal.$(LINK_TARGET).lds
	$(RISCV_GCC) $(RISCV_CFLAGS) $(PROGRAM_C_OBJS) $(PROGRAM_S_OBJS) $(RISCV_LDFLAGS) $(RISCV_LDLIBS) -T$(BSP_DIR)/metal.$(LINK_TARGET).lds -o $@
	$(RISCV_OBJDUMP) --source --all-headers --demangle --line-numbers --wide $@ > $(PROGRAM_LST)
	@mv $(PROGRAM).map $(CONFIGURATION)/
	$(RISCV_SIZE) $@

$(PROGRAM_HEX): $(PROGRAM_ELF)
	$(RISCV_OBJCOPY) -O ihex $(PROGRAM_ELF) $@

.PHONY: clean
clean:
	rm -rf $(CONFIGURATION)
